package cn.dkdoo.utils.config;

import cn.dkdoo.utils.annotation.DkdTransactional;
import cn.dkdoo.utils.common.ThreadLocalPool;
import cn.dkdoo.utils.config.traNo.TraNoBuild;
import cn.dkdoo.utils.config.transactionInfo.TransactionInfoAbstract;
import cn.dkdoo.utils.config.transactionalManager.TransactionalManagerAbstract;
import cn.dkdoo.utils.utils.DkdSpringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;

/**
 * Created by Nicky.Tang on 2021/5/10 11:41 下午
 *
 * @since 02.12.10
 */

@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启aop
@Component
public class DkdTransactionalAspect {
    private static final Logger logger = LoggerFactory.getLogger(DkdTransactionalAspect.class);

    //    @Pointcut(value = "@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    @Pointcut(value = "@within(cn.dkdoo.utils.annotation.DkdTransactional)")
    public void point() {
    }

    @Around("point()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object res = null;
        DkdTransactional dkdTransactionalMthod = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(DkdTransactional.class);
        boolean ifNoTransaction = (dkdTransactionalMthod != null && Propagation.NOT_SUPPORTED.equals(dkdTransactionalMthod.propagation()));
        if (ifNoTransaction) {
            // 不开启事物，就把当前事物挂起
            TransactionInfoAbstract.makeClose();
        }
        if (checkIfNoTransaction(joinPoint, ifNoTransaction)) {
            res = joinPoint.proceed();
            TransactionInfoAbstract.makeOpen();
            return res;
        }
        //todo 目前事物传播级别只有挂起和公用，所以暂时不保存oldTransactionInfo
        boolean isNewOneTransaction = false;
        try {
            String curTraNo = ThreadLocalPool.TRA_NO_MANAGER.getCur();
            if (curTraNo == null) {
                //todo 结合事物传播级别，判断是否要创建新事物
                isNewOneTransaction = newOneTransaction(joinPoint);
            }

            res = joinPoint.proceed();
        } finally {
            if (isNewOneTransaction) {
                //只有创建了新事物那个调用者执行完，才执行提交事务
                try {
                    //可能目标方法操作了多次db,失败了1次，成功了n次，操作成功的数据都要回滚掉
                    TransactionalManagerAbstract transactionalManagerAbstract = DkdSpringUtils.getBeanByClass(TransactionalManagerAbstract.class);
                    transactionalManagerAbstract.commit();
                } finally {
                    ThreadLocalPool.releaseToThread(ThreadLocalPool.TRA_NO_MANAGER, ThreadLocalPool.TRAINFO_MANAGER);
                }
            }
        }
        TransactionInfoAbstract.makeOpen();
        return res;
    }

    private boolean checkIfNoTransaction(ProceedingJoinPoint joinPoint, boolean ifNoTransaction) {
        DkdTransactionConfig traConfig = DkdSpringUtils.getBeanByClass(DkdTransactionConfig.class);
        if (traConfig == null) {
            logger.error("未配置【DkdTransactionConfig】，无法拦截事务。。。");
            return true;
        } else if (ifNoTransaction) {
            logger.info("【传播级别为NOT_SUPPORTED，无需拦截事务。。。");
            return true;
        }
        return false;
    }

    private boolean newOneTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        for (Object arg : joinPoint.getArgs()) {
            if (arg instanceof TraNoBuild) {
                return TraNoBuild.startTra((TraNoBuild) arg);
            }
        }
        return false;
    }

}
